/* 
 * File:   Contact.h
 * Author: RedEyedKiller
 *
 * Created on 6 Ιούλιος 2011, 5:28 πμ
 */

#ifndef CONTACT_H
#define	CONTACT_H

#include "../Mathematics.h"
#include "../Utilities.h"
#include "../PhysicsLogic.h"
#include "../Entity.h"
#include "../Vector2.h"

namespace physicsSystem
{

namespace PhysicsInternals
{

/*
 * This class represents a contact between two physical objects or between 
 * an object and tile map.
 * 
 * Note that contactNormal points from first towards the oposite direction of second.
 * 
 * ex.
 *         *----*   *----*
 *  <-CN--|  F |   |  S |
 *        *----*  *----*
 */
class Contact
{
public:

    /**
     * Used when this Contact is shared between two physical objects
     * @param first The first physical object.
     * @param second The second physical object
     * @param info The info vector calculated by collision detection step.
     */
    Contact(EntitySystem::PhysicsLogic* first, EntitySystem::PhysicsLogic* second, const Math::Vector2F& info, int groupIndex);

    /**
     * Used when this Contact is shared between a physical object and a tile.
     * @param object
     * @param info
     * @param contactNormal 
     */
    Contact(EntitySystem::PhysicsLogic* object, const Math::Vector2F& info, const Math::Vector2F& contactNormal);

    ~Contact()
    {
    }

    /**
     * Tries to resolve this contact.
     * @param duration
     */
    void Resolve(float duration);

    /**
     * Calculate seperation velocity
     * Seperation velocity show the relative velocity/position between two object.
     * If sepVelocity is above 0 it means that the two object are moving towards each other
     * and their velocities should change.
     * Otherwise the two object move away from each other and so only penetration must
     * be resolved.
     */
    float GetSeparatingVelocity();
    
    /**
     * Call all appropriate script methods.
     */
    void ReportScripts();

    /*
     * Inlined setters and getters
     */

    inline float GetPenetration()
    {
        return penetration;
    }

    inline void AdjustPenetration(float value)
    {
        penetration += value;
    }

    inline Math::Vector2F * const GetMovementArray()
    {
        return movement;
    }

    inline EntitySystem::PhysicsLogic* GetFirst()
    {
        return first;
    }

    inline EntitySystem::PhysicsLogic* GetSecond()
    {
        return second;
    }

    inline Math::Vector2F GetContactNormal()
    {
        return contactNormal;
    }
    
    inline int GetGroup() const
    {
        return group;
    }

private:

    /**
     * Resolves any interpenetration between the two objects.
     * @param duration
     */
    void ResolveInterpenetration(float duration);

    /**
     * Calculates and sets the new velocities of the two objects.
     * @param duration
     */
    void ResolveVelocity(float duration);

    /**
     * The first object of the contact. From this starts the contactNormal.
     */
    EntitySystem::PhysicsLogic* first;

    /**
     * The second object. If this is a Contact with a tile second will be NULL.
     */
    EntitySystem::PhysicsLogic* second;

    /**
     * The combined bounce factor.
     */
    float restitution;

    /**
     * The amount the objects of this have moved.
     */
    Math::Vector2F movement[2];

    /**
     * The amount the two objects overlap each other on contactNormal.
     */
    float penetration;

    /**
     * A vector pointing towards where second pushes first.
     */
    Math::Vector2F contactNormal;
    
    /**
     * The ID of the group this contact belongs in.
     */
    int group;

};

};
};
#endif	/* CONTACT_H */

